﻿#pragma once
#include "discrete_face_utils.hxx"


/**
 * @class LoopProcessor
 *
 * @brief Class for processing loops in the face discretization process.
 *
 * @Motivation
 *      When the point set obtained from edge dispersion is projected to the parameter domain, it may jump on the periodic boundary, or it may act as a separation ring without forming a closed region, or even cross the periodic boundary several times.
 *  LoopPorcessor is used to generate all closed discrete loops in the parameter domain for sampling and triangulation, and the algorithm can handle all cases uniformly.
 *
 * @Condition
 *  - The algorithm requires that when an edge crosses a periodic boundary, there must be a point on the periodic boundary, which is guaranteed by the post-processing CheckBoundary function with discrete edges
 *  - It is agreed that the upper bound of the periodic boundary is the left bound and the lower bound is the right bound. (This is the opposite on the intuitive parameter space)
 *  - 2D parameter points are organized by a linked list
 *
 * The main flow of the algorithm
 *  1. Get each loop of the surface and get the coedge from it
 *  2. Obtain the edge discrete result point set corresponding to the coedge, and project it into the parameter field according to the right-hand rule
 *  3. For the 2D point set projected to the parameter field, correct the point order, deduplicate, fix its correct position on the periodic boundary, and determine the direction vector from each point to the next point
 *  4. Interrupt the loop point set that crosses the boundary into several segment point sets that do not cross the boundary, and the start or end point of each segment will be inserted on the periodic boundary
 *  5. Each boundary determines whether adjacent points are connected according to the direction of the inserted point
 *  6. Since a closed loop must be formed, starting from any point, so can use a hash to mark and extract closed loops from the set
 */
class LoopProcessor {
private:
    struct CornerPPoints {
        PPointPtr bot_left, top_left, top_right, bot_right;
    };
    BoundaryAnalyzer boundary_analyzer_;  ///< Boundary analyzer for the face.

    ENTITY_LIST face_loops_;  ///< List of face loops.

    FaceterFaceInfo info_;  ///< Information about the face.

    std::unordered_set<PPointPtr> ppoint_set_;  ///< Set of points.

    std::vector<bndy_type> singular_set_;  ///< Set of singular points.

    std::array<std::map<double, std::vector<std::pair<PPointPtr, Direct>>>, 2> period_edge_u;  ///< Periodic edges in U direction.

    std::array<std::map<double, std::vector<std::pair<PPointPtr, Direct>>>, 2> period_edge_v;  ///< Periodic edges in V direction.

    CornerPPoints corner_ppoints;  ///< Four corner points.

    bool has_periphery = false;  ///< Flag indicating if there is a periphery.

    bool no_boundary{ true };  ///< Flag indicating if there is no boundary.

    std::function<std::vector<SPApar_pos>(SPApar_pos, SPApar_pos)> singularity_generator_{ nullptr };  ///< Generator function To generate sample points between two singularity points in the plane.

    std::function<std::vector<SPApar_pos>(SPApar_pos, SPApar_pos)> generator_;  ///< Generator function To generate sample points between two points in the plane.

    constexpr int GetIndex(Direct dir) { return static_cast<int>(dir); }
    /**
     * @brief Get the discrete loop from a given loop.
     * @param lp Pointer to the loop.
     * @return Pointer to the start of a discrete loop.
     */
    PPointPtr GetDiscreteLoop(LOOP* lp);

    /**
     * @brief Get points from a coedge.
     * @param coedge Pointer to the coedge.
     * @return Three return values, the head pointer and tail pointer of the point linked list obtained from Coedge, whether there is a singularity
     */
    auto GetPPointsFromCoedge(COEDGE* coedge);

    /**
     * @brief Deduplicate points.
     * @param start Starting point.
     * @return Pointer to the start of filtered points.
     */
    PPointPtr PPointsFilter(PPointPtr start);

    /**
     * @brief Correct the position of the point on the period boundary.
     * @param start Starting point.
     * @return Pointer to the start of fixed points.
     */
    PPointPtr PPointsFixer(PPointPtr start);

    /**
     * @brief Handle a Cross singular point.
     * @param prev Previous point.
     * @param curr Current point,also a singularity.
     * @param next Next point.
     */
    template <ParamDirect X> void HandleCrossSingularPoint(PPointPtr prev, PPointPtr curr, PPointPtr next);
    /**
     * @brief Process point linked lists with singularities.
     * @param start Starting point.
     */
    void ProcessSingularPoints(PPointPtr start);

    /**
     * @brief Split the loop into segments.
     * @tparam X Direction parameter.
     * @param period_edge Periodic edges.
     * @param start Starting point.
     */
    template <ParamDirect X> std::vector<PPointPtr> LoopSplit(std::array<std::map<double, std::vector<std::pair<PPointPtr, Direct>>>, 2>& period_edge, PPointPtr start);

    /**
     * @brief Connect the loops.
     * @tparam X Direction parameter.
     * @param period_edge Periodic edges.
     */
    template <ParamDirect X> void LoopsConnect(std::array<std::map<double, std::vector<std::pair<PPointPtr, Direct>>>, 2>& period_edge);

    void ConnectTwoPointByGenerator(PPointPtr p1, PPointPtr p2, std::function<std::vector<SPApar_pos>(SPApar_pos, SPApar_pos)> generator);

public:
    /**
     * @brief Constructor with a face.
     * @param f Pointer to the face.
     */
    explicit LoopProcessor(FACE* f);

    /**
     * @brief Registers a generator function to generate sample points between two points in the plane.
     * @param generator The generator function that takes two SPApar_pos parameters and returns a vector of sample points from the first point to the second point.
     */
    void RegisterGenerator(std::function<std::vector<SPApar_pos>(SPApar_pos, SPApar_pos)> generator) { generator_ = generator; }

    /**
     * @brief Registers a singularity generator function to generate sample points between two singularity points in the plane.
     * @param generator The generator function that takes two SPApar_pos parameters and returns a vector of sample points from the first point to the second point.
     */
    void RegisterSingularityGenerator(std::function<std::vector<SPApar_pos>(SPApar_pos, SPApar_pos)> generator) { singularity_generator_ = generator; }

    /**
     * @brief Execute the loop processing.
     * @return Resulting points after processing.
     */
    void Execute();

    void Extract();

    std::vector<SPApar_pos> points;

    std::vector<int> points_mapping;

    std::vector<std::vector<int>> loops_indexs;
};